﻿// Lesson25_2.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include "pch.h"
#include <iostream>
#include <atomic>
#include <thread>
#include <mutex>
#include <chrono>
#include <vector>
#include <sstream>
#include <queue>
using namespace std::chrono;
#include <Windows.h>

struct MyLock
{
	explicit MyLock(std::mutex& m):
		MyMutex(m)
	{
		MyMutex.lock();
	}
	~MyLock()
	{
		MyMutex.unlock();
	}
	std::mutex& MyMutex;
};

std::mutex counter_mutex;

void increment(int& counter)
{
	int result = 0;
	for (size_t i = 0; i < 100; ++i)
	{
		++result;
		std::this_thread::sleep_for(1ms);
	}
	std::lock_guard<std::mutex> lg(counter_mutex/*,std::adopt_lock*/);
	counter += result;
}

//std::mutex cout_mutex;
std::timed_mutex cout_timed_mutex;


void initializeSharedResources()
{
	std::cout << "init shared resource!" << std::endl;
}

void processFunction(std::once_flag& once_flag,int j)
{
	std::call_once(once_flag,initializeSharedResources);
	//std::scoped_lock lck(cout_mutex);//RAII
	for (size_t i = 0; i < j+1; i++)
	{
		std::unique_lock tlck(cout_timed_mutex,100ms);
		if (tlck)
		{
			std::cout << "thread:" << j << " is do some thing" << std::endl;
		}
	}
}

class StaticTest
{
public:
	StaticTest()
	{
		std::cout << "Construct" << std::endl;
	}
	
	void doTest(int i)
	{
		std::scoped_lock lck(cout_mutex);
		std::cout << i<<" DoTest" << std::endl;
	}
public:
	static std::mutex cout_mutex;
};

std::mutex StaticTest::cout_mutex;

StaticTest& GetStaticTestInstance()
{
	static StaticTest gStaticTest;
	return gStaticTest;
}


void StaticProcess(int i)
{
	GetStaticTestInstance().doTest(i);
}

std::atomic<bool> gInited(false);
std::mutex gMutex;

void processFunctionDoubleCheck(int i)
{
	if (!gInited)
	{
		std::unique_lock lock(gMutex);
		if (!gInited)
		{
			initializeSharedResources();
			gInited = true;
		}
	}

	std::stringstream ss;
	ss << i << " process double check" << std::endl;
	std::cout <<  ss.str();
}

class Producer
{
public:
	Producer(std::queue<std::string>& q, std::mutex& m, std::condition_variable& c)
		:mQueue(q),mMutex(m), mCondVar(c)
	{

	}
	void operator()()
	{
		while (true)
		{
			
			{
				std::unique_lock lck(mMutex);
				time_t cur_t = time(nullptr);
				mQueue.push(std::to_string(cur_t));
				mCondVar.notify_all();
			}
			std::this_thread::sleep_for(1s);
		}
	}
	std::condition_variable& mCondVar;
	std::queue<std::string>& mQueue;
	std::mutex& mMutex;
};

class Comsumer
{
public:
	Comsumer(std::queue<std::string>& q, std::mutex& m, std::condition_variable& c)
		:mQueue(q), mMutex(m), mCondVar(c)
	{

	}
	void operator()()
	{
		while (true)
		{
			std::queue<std::string> tempQueue;
			{
				std::unique_lock lck(mMutex);
				mCondVar.wait(lck, [this] {return !mQueue.empty(); });
				std::swap(tempQueue,mQueue);
			}
			
			while (!tempQueue.empty())
			{
				std::stringstream ss;
				ss<< tempQueue.front() << std::endl;
				std::cout << ss.str();
				tempQueue.pop();
			}
		}
	}
	std::condition_variable& mCondVar;
	std::queue<std::string>& mQueue;
	std::mutex& mMutex;
};




int main()
{
	//int counter = 0;
	//std::vector<std::thread> threads;
	//for (size_t i = 0; i < 10; i++)
	//{
	//	threads.push_back(std::thread{ increment,std::ref(counter) });
	//}

	//for (size_t i = 0; i < threads.size(); i++)
	//{
	//	threads[i].join();
	//}
	//std::cout << "Last Counter result=" << counter << std::endl;

	//std::once_flag oneflag;

	//std::vector<std::thread> threads;
	//for (size_t i = 0; i < 10; i++)
	//{
	//	threads.push_back(std::thread{ processFunction,std::ref(oneflag),i });
	//}

	//for (size_t i = 0; i < threads.size(); i++)
	//{
	//	threads[i].join();
	//}

	//std::vector<std::thread> threads;
	//for (size_t i = 0; i < 10; i++)
	//{
	//	threads.push_back(std::thread{ StaticProcess,i });
	//}

	//for (size_t i = 0; i < threads.size(); i++)
	//{
	//	threads[i].join();
	//}

	//std::vector<std::thread> threads;
	//for (size_t i = 0; i < 10; i++)
	//{
	//	threads.push_back(std::thread{ processFunctionDoubleCheck,i });
	//}

	//for (size_t i = 0; i < threads.size(); i++)
	//{
	//	threads[i].join();
	//}

	std::condition_variable mCondVar;
	std::queue<std::string> mQueue;
	std::mutex mMutex;

	std::thread p{ Producer(mQueue,mMutex,mCondVar) };

	std::thread c{ Comsumer(mQueue,mMutex,mCondVar) };

	p.join();
	c.join();

	return 0;
}

// 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
// 调试程序: F5 或调试 >“开始调试”菜单

// 入门提示: 
//   1. 使用解决方案资源管理器窗口添加/管理文件
//   2. 使用团队资源管理器窗口连接到源代码管理
//   3. 使用输出窗口查看生成输出和其他消息
//   4. 使用错误列表窗口查看错误
//   5. 转到“项目”>“添加新项”以创建新的代码文件，或转到“项目”>“添加现有项”以将现有代码文件添加到项目
//   6. 将来，若要再次打开此项目，请转到“文件”>“打开”>“项目”并选择 .sln 文件
